﻿// Inner Fire 游戏引擎库
// LUA工具集
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 用于统一管理LUA的环境，尽量把LUA封装起来
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-03-25

#pragma once

extern "C" {
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
}
#include <DirectXMath.h>
#include <EASTL/unordered_map.h>
#include <functional>
#include <klog.h>
#include <static_string.h>

// 一些快捷方式
#define L_THIS(ClassName) ikit::lua_get_self<ClassName>(L, #ClassName "Meta")
#define L_FLOAT(idx) static_cast<float>(lua_tonumber(L, idx))
#define L_VEC2(idx)                                                            \
  DirectX::XMFLOAT2(static_cast<float>(lua_tonumber(L, idx)),                  \
      static_cast<float>(lua_tonumber(L, idx + 1)))
#define L_VEC3(idx)                                                            \
  DirectX::XMFLOAT3(static_cast<float>(lua_tonumber(L, idx)),                  \
      static_cast<float>(lua_tonumber(L, idx + 1)),                            \
      static_cast<float>(lua_tonumber(L, idx + 2)))
#define L_INT(idx) static_cast<int>(lua_tointeger(L, idx))
#define L_STR(idx) luaL_checkstring(L, idx)
#define L_BOOL(idx) static_cast<bool>(lua_toboolean(L, idx))
#define L_OBJ(idx, ClassName)                                                  \
  ikit::lua_get_userdata<ClassName>(L, idx, #ClassName "Meta")

namespace ikit {

class LuaEnv {
public:
  LuaEnv();
  LuaEnv(const LuaEnv& lhs) = delete;
  LuaEnv& operator=(const LuaEnv& lhs) = delete;
  ~LuaEnv();

  // 运行一个文件
  bool Run(const char* filename);
  // 注册一个函数，返回注册的ID
  int Reg(ikit::sstr func_name);
  // 使一个注册函数处于可执行的项目
  bool Ready(int reg_id);
  // 执行一个函数（需要事先调用Ready）
  bool CallFunc(int reg_id, int args_count = 0, int result_count = 0);
  // 执行一个函数（设这个函数没有参数，所以能够直接调用）
  bool Call(ikit::sstr func_name, int result_count = 0);
  // 执行一个函数（设这个函数只有一个Int参数）
  bool Call_Int(ikit::sstr func_name, int param_value, int result_count = 0);
  // 执行一个函数（设这个函数只有一个Int参数）
  bool Call_Float(
      ikit::sstr func_name, float param_value, int result_count = 0);
  // 执行一个函数（设这个函数只有一个String参数，一个Int参数，一个float参数）
  bool Call_Button(ikit::sstr func_name, ikit::sstr button_id, int int_param,
      float float_param, int result_count = 0);
  // 执行一个函数，设参数为Int2
  bool Call_Int2(ikit::sstr func_name, int p1, int p2, int result_count = 0);
  // 执行一个函数，设参数为FLOAT2
  bool Call_Float2(
      ikit::sstr func_name, float p1, float p2, int result_count = 0);
  // 执行一个函数，设参数为FLOAT3
  bool Call_Float3(
      ikit::sstr func_name, float p1, float p2, float p3, int result_count = 0);
  // 执行一个函数，设参数为FLOAT4
  bool Call_Float4(ikit::sstr func_name, float p1, float p2, float p3, float p4,
      int result_count = 0);

  // 关闭项目
  void Close();

  // 是否有效
  bool IsValid() const { return ls_ != nullptr; }

  //
  // 属性访问
  //
  lua_State* GetState() const { return ls_; }

protected:
  // 注册系统函数
  void RegisterSystemFunctions(void);

  lua_State* ls_;
  eastl::unordered_map<size_t, int> register_funcs_;

  // 记录上一次获得REG的项目，在Call的时候进行错误的输出
  ikit::sstr last_func_;
};

// 注册一个单元测试

// 将一个Lua的Print打印到一个BUFFER中
int lua_sprintf(lua_State* L, char* buffer, int max);

// 返回一个静态字符串，因为静态字符串在内存中有位置，所以不需要重新分配
void lua_push_sstr(lua_State* L, ikit::sstr str);

// Push一个UserData，如果为空，则Push nil。
template <typename T>
void lua_push_object(lua_State* L, T* obj, const char* meta_name) {
  if (obj) {
    *(T**)lua_newuserdata(L, sizeof(T*)) = obj;
    if (meta_name) {
      // 因为lua_newuserdata导致现在栈顶是obj（-1），然后这个元表就是-2。
      luaL_getmetatable(L, meta_name); // 关联元表
      lua_setmetatable(L, -2);
    }
  } else {
    lua_pushnil(L);
  }
}

// 设置一个UserData做为返回值
// 这个函数始终返回1，做为只返回一个项目时的快捷语法糖。
template <typename T>
int lua_return_object(lua_State* L, T* obj, const char* meta_name) {
  if (obj) {
    *(T**)lua_newuserdata(L, sizeof(T*)) = obj;
    if (meta_name) {
      // 因为lua_newuserdata导致现在栈顶是obj（-1），然后这个元表就是-2。
      luaL_getmetatable(L, meta_name); // 关联元表
      lua_setmetatable(L, -2);
    }
  } else {
    lua_pushnil(L);
  }
  return 1;
}

// 返回一个XMFLOAT2
int lua_return_float2(lua_State* L, const DirectX::XMFLOAT2& value);
// 返回一个XMFLOAT3
int lua_return_float3(lua_State* L, const DirectX::XMFLOAT3& value);

// 返回一个Nil的快捷方式
int lua_return_nil(lua_State* L);

// 将一个self提取出来（使用元表)
template <typename T>
T* lua_get_self(lua_State* L, const char* metatable_name) {
  T* ptr = *(T**)luaL_checkudata(L, 1, metatable_name);
  if (!ptr) {
    KERROR("Invalid 'self' in %s", metatable_name);
  }
  return ptr;
}

// 将一个指定的项目提取出来（使用元表)
template <typename T>
T* lua_get_userdata(lua_State* L, int stack_id, const char* metatable_name) {
  T* ptr = *(T**)luaL_checkudata(L, stack_id, metatable_name);
  if (!ptr) {
    KERROR("Invalid 'userdata' in %s", metatable_name);
  }
  return ptr;
}

// 将一个指定的项目提取出来（不检查)
template <typename T> T* lua_get_no_check(lua_State* L, int stack_id) {
  T* ptr = *(T**)lua_touserdata(L, stack_id);
  if (!ptr) {
    KERROR("Invalid 'userdata' in param %d", stack_id);
  }
  return ptr;
}

// 得到一个Float
inline float lua_get_float(lua_State* L, int stack_id) {
  return static_cast<float>(lua_tonumber(L, stack_id));
}

// 注册一个元表
void lua_register_metatable(
    lua_State* L, const char* metatable_name, luaL_Reg* methods);
// 注册一个元表，继承自一个元表
void lua_register_metatable_inheritance(lua_State* L,
    const char* metatable_name, const char* parent_metatable_name,
    luaL_Reg* methods);

template <typename T>
void lua_set_global_object(
    lua_State* L, T* obj, const char* object_name, const char* metatable_name) {
  // 分配userdata内存并存储Assets指针
  T** udata = (T**)lua_newuserdata(L, sizeof(T*));
  *udata = obj; // 绑定C++对象指针

  // 关联元表
  luaL_getmetatable(L, metatable_name);
  lua_setmetatable(L, -2); // 设置元表并自动弹出栈顶的元表

  lua_setglobal(L, object_name); // 设为全局变量
}

// 将一个指针设置为field
template <typename T>
void lua_set_obj(lua_State* L, const char* field, T* obj, const char* meta) {
  if (obj) {
    // 假设已经注册了"AnimationBlendMeta"元表
    *(T**)lua_newuserdata(L, sizeof(T*)) = obj;

    // 设置元表
    luaL_getmetatable(L, meta);
    lua_setmetatable(L, -2);

    // 将用户数据存入表中
    lua_setfield(L, -2, field);
  } else {
    // 如果没有obj，推送nil
    lua_pushnil(L);
    lua_setfield(L, -2, field);
  }
}

void lua_set_string(lua_State* L, const char* field, const char* string);
void lua_set_int(lua_State* L, const char* field, int value);
void lua_set_float(lua_State* L, const char* field, float value);

template <typename T>
void lua_set_items_table(lua_State* L, const char* field,
    eastl::vector<T>& items,
    std::function<void(lua_State* L, T&, int)> push_content) {

  // 创建表
  lua_newtable(L);

  int index = 1;
  for (auto& i : items) {
    // 为每个连招创建一个子表
    lua_newtable(L);

    // 项目的实体
    push_content(L, i, index);

    // 关键修正：使用固定索引
    lua_rawseti(L, -2, index); // 将子表存入主表[index]位置
    index++;
  }

  // 将表添加到根表
  lua_setfield(L, -2, field);
}

} // namespace ikit